package com.jzo2o.orders.manager.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.market.dto.request.CouponUseReqDTO;
import com.jzo2o.api.market.dto.response.CouponUseResDTO;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.constants.MqConstants;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.IdUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.interceptor.UserContextInteceptor;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusChangeEventEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.manager.client.CustomerClient;
import com.jzo2o.orders.manager.client.MarkerClient;
import com.jzo2o.orders.manager.client.ServeClient;
import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.porperties.TradeProperties;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import com.jzo2o.rabbitmq.client.RabbitClient;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * <p>
 * 下单服务类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {
   @Autowired
   private CustomerClient customerClient;
   @Autowired
   private ServeClient serveClient;
   @Autowired
   private RabbitClient rabbitClient;
   @Autowired
   private OrderStateMachine orderStateMachine;

   //注入自身
   @Autowired
   private IOrdersCreateService owner;
    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO dto) {
        //1. 获取服务地址信息（feign接口：调用customer），并判断
        AddressBookResDTO address = customerClient.findAddressDetailById(dto.getAddressBookId());
        if(address==null) {
            throw new BadRequestException("未选择服务地址");
        }

        //2. 获取服务信息（feign接口：调用fundations），并判断
        ServeAggregationResDTO serve = serveClient.findServeDetailById(dto.getServeId());
        if(serve==null || serve.getSaleStatus()!= 2) {
            throw new BadRequestException("服务数据有问题");
        }

        //3. 组装订单信息
        //3.1 订单基本信息（包括订单号）
        Orders orders = new Orders();
        orders.setId(this.generateOrderId());//订单id
        orders.setOrdersStatus(OrderStatusEnum.NO_PAY.getStatus());//订单状态：默认未支付
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());//支付状态：默认未支付
        orders.setDisplay(1);//用户端是否展示，1：展示，0：隐藏，默认为1
        orders.setServeStartTime(dto.getServeStartTime());//服务开始时间
        orders.setSortBy(DateUtils.toEpochMilli(dto.getServeStartTime())+orders.getId()%10000);//根据服务开始时间转为毫秒时间戳+订单后5位

        //3.2 支付信息 - 可暂时不管

        //3.3 下单人信息
        orders.setUserId(UserContext.currentUser().getId());
        String serveAddress=new StringBuffer()
                .append(address.getProvince())//省
                .append(address.getCity())  //市
                .append(address.getCounty())  //区
                .append(address.getAddress())
                .toString();
        orders.setServeAddress(serveAddress);//订单服务地址
        orders.setContactsName(address.getName());//用户姓名
        orders.setContactsPhone(address.getPhone());//用户手机号
        orders.setLat(address.getLat());//纬度
        orders.setLon(address.getLon());//经度
        //3.4 价格信息
        orders.setPurNum(dto.getPurNum());//个数
        orders.setTotalAmount(serve.getPrice().multiply(new BigDecimal(dto.getPurNum())));//总价
        orders.setDiscountAmount(BigDecimal.ZERO);//优惠价
        orders.setRealPayAmount(orders.getTotalAmount().subtract(orders.getDiscountAmount()));//实付款
        //3.5 服务信息
        orders.setServeId(serve.getId());//服务id
        orders.setPrice(serve.getPrice());//单价
        orders.setServeTypeId(serve.getServeTypeId());//服务类型id
        orders.setServeTypeName(serve.getServeTypeName());//服务类型名称
        orders.setServeItemId(serve.getServeItemId());//服务项id
        orders.setServeItemName(serve.getServeItemName());//服务项名称
        orders.setServeItemImg(serve.getServeItemImg());//服务项图片
        orders.setUnit(serve.getUnit());//服务单位
        orders.setCityCode(serve.getCityCode());//城市编码

        //4. 创建订单
        if(dto.getCouponId()==null) {
            //不使用优惠券
            owner.add(orders);
        }else{
            //使用优惠券
            owner.addWithCoupon(orders,dto.getCouponId());
        }
        String s = orders.getId().toString() + "," + UserContext.currentUserId();
        rabbitClient.sendMsg(MqConstants.Exchanges.ORDERS,
                MqConstants.RoutingKeys.ORDERS_CANCEL_KEY,
                s,
                60000,
                IdUtils.getSnowflakeNextId(),
                false
                );
        //下单时启动状态机
        return new PlaceOrderResDTO(orders.getId());
    }
    @Transactional(rollbackFor = Exception.class)
    public void add(Orders orders){
        boolean save = this.save(orders);
        if(!save){
            throw new DBException("预约下单失败");
        }
        OrderSnapshotDTO orderSnapshotDTO = BeanUtils.copyBean(orders, OrderSnapshotDTO.class);
        orderStateMachine.start(orders.getUserId(),orders.getId().toString(),orderSnapshotDTO);
    }
    @Autowired
    private MarkerClient markerClient;
    @Override
    @GlobalTransactional
    public void addWithCoupon(Orders orders, Long couponId) {
        CouponUseReqDTO reqDTO = new CouponUseReqDTO();
        reqDTO.setId(couponId);
        reqDTO.setOrdersId(orders.getId());
        reqDTO.setTotalAmount(orders.getTotalAmount());
        CouponUseResDTO use = markerClient.use(reqDTO);
        BigDecimal amount = use.getDiscountAmount();
        orders.setDiscountAmount(amount);
        orders.setRealPayAmount(orders.getTotalAmount().subtract(orders.getDiscountAmount()));//实付款
        boolean save = this.save(orders);
        if(!save){
            throw new DBException("预约下单失败");
        }
        OrderSnapshotDTO orderSnapshotDTO = BeanUtils.copyBean(orders, OrderSnapshotDTO.class);
        orderStateMachine.start(orders.getUserId(),orders.getId().toString(),orderSnapshotDTO);
    }

    @Autowired
   private RedisTemplate redisTemplate;
    private static final String ORDER_SERIAL_NUM_KEY = "ORDER:SERIAL:NUM";
    @Override
    public Long generateOrderId() {
        long idInit = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd") * 10000000000000L;
        Long id=redisTemplate.opsForValue().increment(ORDER_SERIAL_NUM_KEY)+idInit;
        return id;
    }

    /**
     * 订单支付
     * @param id
     * @param ordersPayReqDTO
     * @return
     */
    @Autowired
    private TradeProperties tradeProperties;
    @Autowired
    private NativePayApi nativePayApi;
    @Override
    public OrdersPayResDTO pay(Long id, OrdersPayReqDTO ordersPayReqDTO) {
        Orders orders = this.getById(id);
        //判断订单是否存在
        if(orders==null){
            throw new BadRequestException("订单不存在");
        }
        //判断订单是否支付
        if(orders.getPayStatus().equals(OrderPayStatusEnum.PAY_SUCCESS.getStatus())&& ObjectUtils.isNotEmpty(orders.getTradingOrderNo())){
            OrdersPayResDTO resDTO = BeanUtils.copyBean(orders, OrdersPayResDTO.class);
            resDTO.setProductOrderNo(id);
            return resDTO;
        }
        //是否改变过支付渠道
        boolean changeChannel = false;
        if(orders.getTradingChannel()!=null&&ObjectUtils.notEqual(orders.getTradingChannel(),ordersPayReqDTO.getTradingChannel())){
            changeChannel=true;
        }
        //支付渠道
        Long enterpriseId = ObjectUtils.equals(ordersPayReqDTO.getTradingChannel(), PayChannelEnum.WECHAT_PAY) ? tradeProperties.getWechatEnterpriseId() : tradeProperties.getAliEnterpriseId();
        NativePayReqDTO payReqDTO = new NativePayReqDTO();
        //productOrderNo：是业务系统的订单号，本项目就是家政服务的订单号。
        payReqDTO.setProductOrderNo(id);
        //tradingAmount：支付金额
        //payReqDTO.setTradingAmount(orders.getRealPayAmount());
        payReqDTO.setTradingAmount(new BigDecimal(0.01));
        //enterpriseId：商户号，进入微信或支付宝商户平台获取。
        payReqDTO.setEnterpriseId(enterpriseId);
        //memo: 备注信息
        payReqDTO.setMemo(orders.getServeItemName());
        //tradingChannel：微信支付传入WECHAT_PAY，支付宝支付传入ALI_PAY
        payReqDTO.setTradingChannel(ordersPayReqDTO.getTradingChannel());
        //changeChannel：当用户先微信支付，然后又进行支付宝支付表示切换了支付渠道，此时传入true
        payReqDTO.setChangeChannel(changeChannel);
        //productAppId: 业务系统标识，服务调用方的标识字符串
        payReqDTO.setProductAppId(tradeProperties.getProductAppId());
        NativePayResDTO resDTO = nativePayApi.createDownLineTrading(payReqDTO);
        if(ObjectUtils.isEmpty(resDTO)){
            throw new BadRequestException("支付请求失败");
        }
        owner.updateOrders(id,resDTO);
        //OrdersPayResDTO
        OrdersPayResDTO dto = BeanUtils.copyBean(resDTO, OrdersPayResDTO.class);
        dto.setPayStatus(orders.getPayStatus());
        return dto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrders(Long id, NativePayResDTO resDTO) {
        boolean update = this.lambdaUpdate()
                .eq(Orders::getId, id)
                .set(Orders::getTradingOrderNo, resDTO.getTradingOrderNo())
                .set(Orders::getTradingChannel, resDTO.getTradingChannel())
                .update();
        if(!update){
            throw new DBException("修改订单失败");
        }
    }
    @Autowired
    private TradingApi tradingApi;
    @Override
    public OrdersPayResDTO payResult(Long id) {
        Orders orders = this.getById(id);
        if(orders==null){
            throw new BadRequestException("订单不存在");
        }
        if(orders.getTradingOrderNo()==null){
            throw new BadRequestException("错误订单请求");
        }
        OrdersPayResDTO resDTO = BeanUtils.copyBean(orders, OrdersPayResDTO.class);
        resDTO.setProductOrderNo(id);//订单号
        //是否已经支付成功过
        if(orders.getPayStatus().equals(OrderPayStatusEnum.PAY_SUCCESS.getStatus())){
           return  resDTO;
        }
        //调用微信查询订单接口
        TradingResDTO result = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());
        if(ObjectUtils.isNotNull(result) && result.getTradingState() == TradingStateEnum.YJS) {
            //5.支付成功就更新订单表
            owner.paySuccess(id, result.getTransactionId(),result.getPaySuccessTime());
            resDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
            resDTO.setQrCode("");
        }
        return resDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(Long id, String transactionId, LocalDateTime paySuccessTime) {
        OrderSnapshotDTO orderSnapshotDTO=new OrderSnapshotDTO();
        orderSnapshotDTO.setId(id);
        orderSnapshotDTO.setThirdOrderId(transactionId);
        orderSnapshotDTO.setPayTime(paySuccessTime);
        Orders orders = this.getById(id);
        orderStateMachine.changeStatus(orders.getUserId(),id.toString(), OrderStatusChangeEventEnum.PAYED,orderSnapshotDTO);
    }
}
